package com.aegonthtf.sis.config;

import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Map.Entry;
import java.util.Optional;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import org.apache.http.HttpHost;
import org.apache.http.HeaderElement;
import org.apache.http.HeaderElementIterator;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.protocol.HTTP;
import org.apache.http.ssl.SSLContextBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import cn.com.common.util.log.LogUtils;

/**
 * HttpClient配置
 * @author PromethusLiu
 * @date 20191030
 */
@Configuration
@Component
@ConditionalOnClass(value = {RestTemplate.class, CloseableHttpClient.class})
public class HttpClientConfig {


    @Autowired
    private HttpClientPoolConfig httpClientPoolConfig;

    private final Logger LOGGER = LoggerFactory.getLogger(HttpClientConfig.class);


    /**
	 * 创建HTTP客户端工厂
	 */
	@Bean(name = "clientHttpRequestFactory")
	public ClientHttpRequestFactory clientHttpRequestFactory() {

		//maxTotalConnection 和 maxConnectionPerRoute 必须要配
		if (httpClientPoolConfig.getMaxTotalConnect() <= 0) {
			throw new IllegalArgumentException("invalid maxTotalConnection: " + httpClientPoolConfig.getMaxTotalConnect());
	    }
	    if (httpClientPoolConfig.getMaxConnectPerRoute() <= 0) {
			throw new IllegalArgumentException("invalid maxConnectionPerRoute: " + httpClientPoolConfig.getMaxConnectPerRoute());
	    }
	    HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient());
	    // 连接超时
	    clientHttpRequestFactory.setConnectTimeout(httpClientPoolConfig.getConnectTimeout());
	    // 数据读取超时时间，即SocketTimeout
	    clientHttpRequestFactory.setReadTimeout(httpClientPoolConfig.getReadTimeout());
	    // 从连接池获取请求连接的超时时间
	    clientHttpRequestFactory.setConnectionRequestTimeout(httpClientPoolConfig.getConnectionRequestTimout());
	    return clientHttpRequestFactory;
	}


	@Bean
	public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
		return createRestTemplate(factory);
	}


	/**
	 * 配置httpClient
	 * @return
	 */
	@Bean
	public CloseableHttpClient httpClient() {

		HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
	    try {
	    	//设置信任ssl访问
			SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (arg0, arg1) -> true).build();
			httpClientBuilder.setSSLContext(sslContext);
			HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
			SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);

			//注册http和https请求
			Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
					.register("http", PlainConnectionSocketFactory.getSocketFactory())
					.register("https", sslConnectionSocketFactory).build();

			//使用Httpclient连接池的方式配置。
			PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
			//最大连接数
			poolingHttpClientConnectionManager.setMaxTotal(httpClientPoolConfig.getMaxTotalConnect());
			//同路由并发数
			poolingHttpClientConnectionManager.setDefaultMaxPerRoute(httpClientPoolConfig.getMaxConnectPerRoute());
			//配置连接池
			httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);

			//设置长连接保持策略
			//httpClientBuilder.setKeepAliveStrategy(connectionKeepAliveStrategy());
			return httpClientBuilder.build();
	    } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
	    	LogUtils.error(LOGGER,e, "HttpClientConfig.httpClient", "初始化HTTP连接池出错", "捕获异常：{0}", e.getMessage());
	    }

	    return null;
	  }


	/**
	* 配置长连接保持策略
	* @return
	*/
	public ConnectionKeepAliveStrategy connectionKeepAliveStrategy(){
		return (response, context) -> {
			// Honor 'keep-alive' header
			HeaderElementIterator it = new BasicHeaderElementIterator(
					response.headerIterator(HTTP.CONN_KEEP_ALIVE));

			while (it.hasNext()) {

				HeaderElement he = it.nextElement();
				String param = he.getName();
				String value = he.getValue();

				if (value != null && "timeout".equalsIgnoreCase(param)) {
					try {
						return Long.parseLong(value) * 1000;
					} catch(NumberFormatException ignore) {
						LogUtils.error(LOGGER,ignore, "HttpClientConfig.connectionKeepAliveStrategy",
								"解析长连接过期时间异常", "捕获异常：{0}", ignore.getMessage());
					}
				}
			}

			HttpHost target = (HttpHost) context.getAttribute(HttpClientContext.HTTP_TARGET_HOST);

			//如果请求目标地址,单独配置了长连接保持时间,使用该配置

			Optional<Entry<String, Integer>> any = httpClientPoolConfig.getKeepAliveTargetHost().
					entrySet().stream().filter(e -> e.getKey().equalsIgnoreCase(target.getHostName())).findAny();

			//否则使用默认长连接保持时间
			return any.map(en -> en.getValue() * 1000L).orElse(httpClientPoolConfig.getKeepAliveTime() * 1000L);
		};
	}


	private RestTemplate createRestTemplate(ClientHttpRequestFactory factory) {

		RestTemplate restTemplate = new RestTemplate(factory);

		//重新设置StringHttpMessageConverter字符集，解决中文乱码问题
		modifyDefaultCharset(restTemplate);
		//设置错误处理器
		restTemplate.setErrorHandler(new DefaultResponseErrorHandler());

		return restTemplate;
	}


	/**
	 * 修改默认的字符集类型为utf-8
	 *
	 * @param restTemplate
	 */
	private void modifyDefaultCharset(RestTemplate restTemplate) {
		List<HttpMessageConverter<?>> converterList = restTemplate.getMessageConverters();
		HttpMessageConverter<?> converterTarget = null;
		for (HttpMessageConverter<?> item : converterList) {
			if (StringHttpMessageConverter.class == item.getClass()) {
				converterTarget = item;
				break;
			}
		}
		if (null != converterTarget) {
			converterList.remove(converterTarget);
		}
		Charset defaultCharset = Charset.forName(httpClientPoolConfig.getCharset());
		converterList.add(1, new StringHttpMessageConverter(defaultCharset));
	}


}
